# coding:utf-8
# -*- coding: utf-8 -*-
# @Time    : 8/7/20 4:39 PM
# @Author  : Kevin
# @Site    : 
# @File    : fast_data.py
# @Descri  : 
# @Software: pycharm
import os,json,random
# import config
from lxml import etree
from fake_useragent import UserAgent
import sys

# import config as config
import requests
from fdfs_client.client import Fdfs_client, get_tracker_conf

# from fdfs_client.client import Fdfs_client,get_tracker_conf

# from spider import config
try:
    from spider import config
except ImportError:
    sys.path[0] = os.path.dirname(os.path.split(os.path.realpath(__file__))[0])
    from spider import config
print("33333333333")
LOCAL_HOST_PREFIXES = config.LOCAL_HOST_PREFIXES
fdfs_upload_file = os.path.join(config.APP_ROOT, 'database', 'fdfs_upload_file')
client_config_path = os.path.join(config.APP_ROOT, 'database', 'client.conf')
imgs_path = os.path.join(config.APP_ROOT, 'imgs')
# fdfs_upload_file = os.path.join(config.APP_ROOT, 'imgs', 'client.conf')


s = requests.Session()
i = 1
# for imgurl in urls:
# url = "https:" + imgurl
# url = "https://www.ofweek.com/ai/2020-08/ART-201717-11001-30453468.html"
# headers = {'User-Agent': (UserAgent()).random}
# response = s.get(url, headers=headers)
# print("4849",response.text)
# soup = etree.HTML(response.text)
# # bs4_soup = BeautifulSoup(response.text, "lxml")
# # iid = md5(url.encode()).hexdigest()  # 文章URL加密MD5
# # contents = bs4_soup.select('div[id="cnblogs_post_body"]')
# imgs = soup.xpath('//div[@id="articleC"]//img/@src')  # 图片
# print(imgs)


# image = requests.get(url).c
# print(type(client_config_path), client_config_path)
# print(imgs_path)
# print(fdfs_upload_file)
# std = os.popen(
# "/usr/bin/fdfs_upload_file /etc/fdfs/client.conf /home/kevin/work/data_fang/spider/imgs/"+ str(i) + ".jpg").read()
# std = os.popen(fdfs_upload_file + " " + client_config_path + " " + imgs_path + "\\" + str(i) + ".jpg").read()
# print(std)
# print('*********** fastdfs excute start ***********')
# print(type(std.strip()), std.strip())
# img = "<img " + "src="'"' + config.LOCAL_HOST_PREFIXES + std.strip() + '"'">"
# print(img)
# i += 1


# dict_data = {'category': 'article', 'name': 'LIDAR有哪些主要设计考量？', 'url': 'https://www.ednchina.com/news/2020072416623.html', 'abstract': '', 'content': '<p>成功的自动驾驶汽车必定将使用紧密集成的传感器系统来达到甚至超越人类的驾驶能力。人类驾驶员一般利用双眼、双耳，以及车辆运动给人的反馈来驾驶汽车。我们的大脑会实时处理所有这些信息，并从人脑的驾驶经验数据库中直觉反应。复现人类驾驶能力所需的传感器包括雷达、激光雷达(LIDAR)、摄像头、惯性测量单元(IMU)，以及超声波传感器。每种系统都有其优势，也有其缺点。单一传感器的精度和性性能不足以取代所有其他传感器相辅相成的多传感器融合是大趋势。本文将介绍与LIDAR有关的主要设计考量，LIDAR是一种传感器，为各种自动驾驶解决方案提供大量数据。</p><img src="http://file.elecfans.net/group1/M00/00/01/wKgNjl8tNtKAAuNzAADXfr_502I688.jpg"><p>图1.蛛网图，比较视觉、雷达和LIDAR。</p><p>在自动驾驶汽车中，LIDAR与雷达密切合作。这两种技术在工作中不会产生可见光，这对于夜间驾驶或弱光条件至关重要。雷达适合远距离检测和跟踪，LIDAR则提供更高的角分辨率，可以识别对象并对其进行分类。换句话说，雷达适合检测对象是否存在，LIDAR则能够在雷达检测到对象的基础上提供关于该对象的具体信息。</p><img src="http://file.elecfans.net/group1/M00/00/01/wKgNjl8tNtKAUvk1AACpEXYjnzc690.jpg"><p>图2.自动驾驶汽车的LIDAR感知。</p><p>设计LIDAR系统时会面临一些技术挑战，主要挑战之一就是近红外波长要保持在人眼安全限值之下。关于这些安全指南，请参考IEC60825-1。这并不是要降低人眼安全的重要性，本文探讨的所有方面最终都会影响人眼安全。目前存在多种不同的LIDAR系统技术，设计复杂程度各不相同，各有其优点和缺点。</p><p>重要的是，所有设计需要关注的基础方面都相同。再此我们着重讨论人眼安全以外影响系统设计的其他方面，包括：SNR最大化、最小可检测要求、视场、散热、功耗，以及航位推测。</p><p>查看接收链路，会发现系统的信噪比(SNR)会影响在远距离（100米至300米）检测小型目标的能力。ADC噪底不能超过接收路径中的其他噪声源。如果背景光或散粒噪声贡献因素低于ADC的噪底或印刷电路板(PCB)噪声，系统精度就会受限。采用直接飞行时间(ToF)法要求系统可以输出短脉冲（~1ns至5ns），且使用高采样速率ADC检测这些脉冲。采样速率达到1GSPS，即可满足接收信号链路需求。此外，请注意，ADC的有效位数(ENOB)必须支持跨阻放大器(TIA)的整个输出范围，不能对信号实施削波。</p><p>系统需要检测100米开外的篮球吗？确定相关对象的反射率、尺寸和距离，会决定TIA的SNR需求。与ADC相同，TIA也需要检测同样的窄脉冲。因为系统需要处理的对象的距离、反射率和尺寸范围甚广，所以TIA必须能够在饱和后中快速恢复。高反射（比如交通标志怕爱）或近距离目标能反射强光而造成TIA饱和，。这些都是常见事件，而系统恢复的速度（以尽量减少致盲时间）对于安全性至关重要。</p><img src="http://file.elecfans.net/group1/M00/00/01/wKgNjl8tNtKAUkqfAAE5BIz9I-o503.jpg"><p>图3.LIDAR电气架构。</p><p>系统的视场和角分辨率也会影响到检测一个篮球的能力。发射和接收光学是决定视场的主要因素。角分辨率决定您能远距离检测篮球大小的目标并进行分类，还是只能检测目标的存在。</p><p>对于LIDAR系统设计人员来说，处理这些系统的功耗和散热是不小的挑战。当然，降低信号链的功耗会相应降低产生的热量。组件在的性能随温度会出现大幅变化，其中更敏感的一些组件可能需要温度补偿。使用热电控制器是一种不错的方法，可以高度准确地对IC进行冷却或加热。如果追求精确的话，发光和感光二极管都需要温度补偿，以在LIDAR系统的工作温度范围内保持稳定的工作波长和效率。</p><p>在某些情况下，对雪崩光电二极管和激光器施加的偏置电压要到几百伏（正压或负压）。高效生成这些电压，且使用尽可能少的组件，是最佳设计实践所追求的。要提供准确的基准电压源，需要使用精密数模转换器(DAC)生成偏置点、电流和电压。沿着传统的1.8V至12V电压域，LIDAR系统对电压的需求是有所增加的。小心选择电源解决方案可以处理这一问题，尤其是解决方案中额外添加一种电压时。选择具备关断或低功率模式的IC和电源也非常重要，这样的系统可以灵活高能效地轮询多个通道。</p><p>集成LIDAR传感器的IMU具备多种优势。IMU传感器智能地融合多轴陀螺仪和加速度计，为除震和导航应用提供可靠的位置和运动识别。即使在复杂的操作环境下，面临极限运动动力学问题时，精密微机电系统(MEMS)IMU也能提供所需的精度。</p><p>IMU为自动驾驶系统提供航位推测、定位和稳定功能。反过来，在ADAS或GPS性能下降，或者不可用时，上述功能又能为系统提供可靠的数据。IMU能够有效利用高更新速率（每秒数千个样本），且可以不受外部环境变化干扰。IMU越稳定，越能长时间为系统提供关键且可靠的航迹信息。</p><p>IMU可以直接集成到LIDAR模块中，用于检测、分析和纠正车辆运行环境中常见的振动。例如，IMU输出可辅助拼接LIDAR点云，否则，这些点云会因为车辆越过路面坑洼而出现偏离。此外，还可以使用IMU检测旋转式LIDAR系统的轴承磨损，以在实际出现故障之前维修LIDAR，提高安全性。</p><p>结论</p><p>在最初产品定义期间，需要考虑LIDAR系统的复杂性，以确定可接受的SNR、检测要求、视野、散热限制以及功耗。了解哪些组件是各个问题的主要贡献因素，同时谨慎选择IC，可以大大提高设计的成功几率。</p><p>作者简介</p><p>SarvenIpek于2006年加入ADI公司。在ADI公司任职期间，Sarven在故障分析、设计、特性表征、产品工程、项目管理和程序管理方面积累了丰富的经验。Sarven目前是ADI公司麻萨诸塞州威明顿市自动驾驶和安全产品部门LIDAR部的营销经理。</p><p>他拥有东北大学电气和计算机工程学士学位以及电气工程硕士学位，主修通信系统和信号处理。</p>', 'video_id': 'a2bf8d77bd7c2d125f3d3447cd3ea781', 'tag': '', 'company': 454398, 'typeid': ''}
# dict_data = {'category': 'article', 'name': 'JetBot系列教程之自主避障', 'url': 'https://www.waveshare.net/study/article-934-1.html', 'abstract': '', 'content': '<p>Collision-avoidance</p><p>JetBot 的自主避障功能分为三个步骤。收集数据，训练模型和自主避障。这里我们分三部分讲。</p><p>收集数据</p><p>打开Notebooks/collision_avoidance/ 目录下的data_collection.ipynb文件。如果你没有在目录下找到这三个文件，请回到</p><p>JetBot系列教程之安装镜像和配置</p><p>中再照着更新程序部分操作一遍。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7OAJfC9AADltugnxS8504.jpg"><p>如果是前面章节讲过的代码，这里不会详细说。</p><p>单元1，这里是创建摄像头连接，将摄像头画面显示在网页上，注意不要修改摄像头的分辨率设置。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7OAJMbsAAEBJg0ByWM529.jpg"><p>单元2， 创建一个dataset目录，用来存放我们接下来收集的图片。dataset目录下创建两个文件夹，分别是blocked和free。blocked文件夹将用来存放避障场景图片，而free文件将用来存放畅通场景图片。什么？不知道什么是避障场景，什么是畅通场景？好吧，简而言之呢，避障场景，就是小车需要转向避开障碍物的场景，畅通场景就是小车可以直接直行的场景。这里使用一个try/except的结构来避开文件覆盖，如果路径下已经有了这两个文件夹，程序会报错说文件夹已经存在了，并不会覆盖掉老的目录。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7OAGMplAABx3PKn5RU175.jpg"><p>单元3，创建两个按钮和两个显示框，按钮用来操作添加避障场景图片和畅通场景图片的。显示框分别显示的是当前已存放的图片数量，如果你是第一次运行脚本，那你的图片数量应该都是0。不用使劲按按钮，这里的按钮只是用来演示，还没有实际效果。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7OAXDfqAACaU4xUMS4565.jpg"><p>单元4，这里分别写了三个函数。第一个save_snapshot(directory), 这个函数是用来保存图片,会被按键触发程序调用，函数将当前的摄像头图像保存到对应的路径下。</p><p>第二个save_free()函数，对应到上面的add free 绿色按钮，函数中调用保存图片函数，将当前摄像头图像保存到free文件夹中，并更新free的图片数量。</p><p>同理，第三个save_blocked()函数是将当前的图像保存到blocked文件夹中，并更新图片数量。</p><p>最后两个语句是将函数和按键关联起来， 让你在点击按钮的时候执行相应的函数。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7OAAvhpAADmM0NTHLs408.jpg"><p>单元5，这里将按钮和摄像头图像显示在一起，方便你操作。 OK现在开始你就可以将JetBot小车放到你准备做避障的场景中去，分别收集避障场景图和畅通场景图。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7OAPrejAAFqmecg2VE878.jpg"><p>如果当前的场景有障碍物，需要小车转弯的地方，就按下add blocked 红色按键。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7OAMGqJAAHW_rKpHH0522.jpg"><p>如果当前的场景是畅通的，小车可以直行，就按下add free绿色按钮。就这样分别收集各种图片数据。为了保持较好的避障效果。建议你每种场景至少收集100张图片。（注意不是让你在同一个画面里面狂按100下，拍100张一样的图片，是要你收集100种需要避障和100种可以直走的场景图！！！）</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7OAZlZgAAFMms8uOt4509.jpg"><p>最后一单元，在收集结束之后，运行该单元将你的图片打包。这里打包的目的是因为有的用户可能会使用不同的小车去做后面的模型训练，或者你想将你们的图片数据备份一下。如果你是在同一辆小车上把三个步骤都做完的，那你也可以不打包，直接跳过这个单元。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7OAdLLxAADVXMceNS4924.jpg"><p>数据收集到这里就结束了。有什么问题请留评论哦。^_^</p><p>题外话</p><p>今天的题外话很短哦。你可能感觉每次要按一下，然后跑过去换一下小车的位置，再回来按一下，非常非常非常麻烦和低效，那么看过来。在保持这个程序运行的同时，你可以打开上一章节我们讲到的手柄遥控程序，运行遥控程序，然后用你的手柄去操作小车运行，然后你在电脑端做操作就行了。不用跑来跑去。（没错，小车可以同时运行两个kernel），当然，你也可以复制遥控部分的代码过来这个程序上面一起运行，都是可行的。这里就自由发挥吧。</p><p>模型训练</p><p>打开/train_model.ipynb文件，注意该文件是在Notebooks/collision_avoidance模型下。\r\n单元1， 导入PyTorch函数库。PyTorch是比较通用的一个深度学习库，有兴趣的伙伴可以自行百度或者谷歌了解。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7OAaq4tAAB7n9vA4a8443.jpg"><p>单元2，将数据包解压。注意，如果你是跟我一样在同个小车上做的模型训练，就直接跳过这个单元就可以了。因为你的数据已经是保存在当前的目录下，如果再运行这个单元2，会一直卡住。</p><p>那如果你是在不同的小车上做的训练的话，你需要把你之前收集打包的那个数据包复制到当前的目录下，然后再运行这个单元2去解压出来。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7OATQE4AABUklqA2m8972.jpg"><p>单元3，这里使用ImgeFolder类，将我们的数据包转化，方便后面做训练</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SATTxFAACKtYF7vT4752.jpg"><p>单元4，将数据包分成两组，分别是训练组和测试组. 测试组用来验证模型的精确度</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SAISqJAABgqwbPato160.jpg"><p>单元5， 这里会创建两个实例，用于后面混排数据并生成图片。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SAAxk0AACCQhdi8BE860.jpg"><p>单元6，定义神经网络。在转移学习（transfer learning）中，我们可以使用预训练的模型来训练新的任务，这样我们可以直接使用预训练的模型中一些已经学习到的功能，然后训练新的任务，这样会节省很多功夫。总结来说呢，就是好比教导一个中学生高中知识，会比教导一个小学知识都没有的小孩子高中知识会来得简单和快速的道理一样（小编纯属瞎掰，O(∩_∩)O哈哈~）。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SATa4pAACCBpRy1S8655.jpg"><p>单元6运行之后，会自动下载Alexnet模型，这里会花费一点时间，需要耐心等一下。（目前好像出现下载中断报错的情况，你可以选择根据提示里面的网址直接下载，然后再手动复制到提示里面提到的路径中去）</p><p>单元7，下载的模型是有1000个labels，但是我们需要判断的就只有两种情况，避障和直行，也就是我们只需要两个label.所以这里需要处理一下。</p><p>单元8，将模型转换，以便在GPU上运行。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SACi9kAABLIjZU1ZU471.jpg"><p>单元9，最后一单元就是模型训练了，这里会做30组训练，训练结束之后会生成一个best_model.pth的模型文件。等待训练完成即可。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SAKNgzAADvcz4Tshs526.jpg"><p>自主避障</p><p>做完前面的准备工作之后，这里我们就可以来做避障的测试，检验一下你的数据和模型做的如何。\r\n打开Notebooks/collision_avoidance路径下的live_demo.ipynb文件\r\n单元1， 还是初始化PyTorch模型。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SAOFx3AAB0tXnmUyQ833.jpg"><p>单元2，加载前面训练好的模型文件</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SAAqOaAAA935i5If0726.jpg"><p>单元3，将模型加载到CPU内存，准备传入GPU设备中做计算</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SAKOd6AABAbFJK2uI722.jpg"><p>单元4，处理摄像头图像数据，便于后面做运算</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SAdTurAACVn2CJxks767.jpg"><p>单元5，将摄像头图像显示在网页上，并同时用一个滑动条来显示当前图片的避障值</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SALnh9AACgkHmnRSY985.jpg"><p>单元6，调用Robot库，准备小车的运动</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SAboYnAABAVif7fQI451.jpg"><p>单元7，根据避障值，来操作小车是直行还是转弯（避障）。如果你在做避障的时候，觉得小车速度太快可以降低robot.forward()里面的参数值来降低小车的前行速度，降低robot.left()里面的参数值来降低小车的转弯幅度。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SAGSOBAAC7cXwmc9U818.jpg"><p>单元8，实时更新摄像头数据，便于观察</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SARlH1AABbW4aaNoY266.jpg"><p>最后几个单元，如果你的小车运行不正常，或者你想要停下小车，可以接着运行。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86I7SAdT0tAAB_rOp7X18654.jpg"><p>最后这里演示一下避障效果吧：</p><p>\r\n\t\t\t</p><p>\r\n\t\t\t</p><p>\r\n\t\t\t</p><p>\r\n\t\t\t</p>', 'video_id': 'b915816349a38970c285728dfccabf72', 'tag': '', 'company': 454398, 'typeid': ''}
# dict_data = {'category': 'article', 'name': '树莓派Qt系列教程12：布局管理(下)', 'url': 'https://www.waveshare.net/study/article-1015-1.html', 'abstract': '', 'content': '<p>前言</p><p>上一节中给大家讲了布局管理的几种方法，还剩下最后一种，布局管理器。这一节我们就来探究一下它的庐山真面目。如果有学习过Qt Widgets的小伙伴，肯定了解Qt提供了QLayout类及其子类来作为布局管理器。在QML中的布局管理器其实与Qt Widgets类似。我们一起来学习下吧。</p><p>布局管理器</p><p>从Qt5.1开始，新提供的Qt Quick Layouts模块提供了几个布局管理器项目。它们非常适合可调整大小的用户界面，布局管理器包括</p><p>ColumnLayout</p><p>、</p><p>RowLayout</p><p>、</p><p>GridLayout</p><p>和</p><p>StackLayout</p><p>。</p><p>因为布局管理器作为单独的模块提供，所以，在使用前需要添加导入语句</p><p>import QtQuick.Layouts 1.3</p><p>1、RowLayout</p><p>RowLayout会在水平方向上布局，而对于其中具体的布局设计，需要通过Layout附加属性来进行。属性如下：</p><p>Layout.minimumWidth：设置最小宽度</p><p>Layout.minimumHeight：设置最小高度</p><p>Layout.preferredWidth：设置建议的宽度</p><p>Layout.preferredHeight：设置建议的高度</p><p>Layout.maximumWidth：设置最大宽度</p><p>Layout.maximumHeight：设置最大高度</p><p>Layout.fillWidth：设置填充宽度，当界面被拉伸出现更多空间时，该控件的宽度会自动增加来填充这些空间</p><p>Layout.fillHeight：设置填充高度</p><p>Layout.alignment：设置对其方式 我们通过一个简单的小例程来看一下：</p><p>    RowLayout{\r\n        anchors.fill: parent\r\n        spacing: 20\r\n\r\n\r\n        Rectangle{\r\n            Layout.alignment: Qt.AlignTop\r\n            color: "orange"\r\n            Layout.preferredWidth: 100\r\n            Layout.preferredHeight: 100\r\n        }\r\n\r\n        Rectangle{\r\n            Layout.alignment: Qt.AlignTop\r\n            color: "blue"\r\n            Layout.preferredWidth: 100\r\n            Layout.preferredHeight: 100\r\n        }\r\n\r\n        Rectangle{<br><br>            Layout.alignment: Qt.AlignBottom\r\n            color: "green"\r\n            Layout.preferredWidth: 100\r\n            Layout.preferredHeight: 100\r\n        }\r\n\r\n        Rectangle{\r\n            Layout.alignment: Qt.AlignBottom\r\n            color: "purple"\r\n            Layout.preferredWidth: 100\r\n            Layout.preferredHeight: 100\r\n        }\r\n    }</p><p>在我们导入了Layout模块后，就可以在程序中使用RowLayout元素了，和上一节一样，在布局管理器中添加矩形小框，不同的是这个例程我们要单独设置一下子项目的位置。</p><p>使用Layout.alignment设置对其方式，对于RowLayout来说，不设置的话就是居中对其，整齐的排列在一条直线上，现在我们将前两个矩形向上对其，后两个矩形向下对其。现在运行程序看看，矩形的位置发生了什么变化？</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86RxiAEOjjAAA-tjfgNd0229.jpg"><p>2、ColumnLayout</p><p>ColumnLayout的用法和RowLayout用法类似，向我们常见到的一列选项，就是用了垂直布局管理器，我们也是给出一个小例程供大家参考</p><p>ColumnLayout{\r\n        anchors.centerIn: parent\r\n        spacing: 20\r\n\r\n        Text{\r\n            text: "请选择喜欢的科目"\r\n            font.pointSize: 20\r\n            color: "purple"\r\n            font.bold: true\r\n            Layout.leftMargin: 30\r\n        }\r\n\r\n        CheckBox{\r\n            text: "语文"\r\n            Layout.leftMargin: 30\r\n            font.pixelSize: 15\r\n        }\r\n        CheckBox{\r\n            text: "数学"\r\n            Layout.leftMargin: 30\r\n            font.pixelSize: 15\r\n        }\r\n        CheckBox{\r\n            text: "英语"\r\n            Layout.leftMargin: 30\r\n            font.pixelSize: 15\r\n        }\r\n        CheckBox{\r\n            text: "生物"\r\n            Layout.leftMargin: 30\r\n            font.pixelSize: 15\r\n        }\r\n        CheckBox{\r\n            text: "化学"\r\n            Layout.leftMargin: 30\r\n            font.pixelSize: 15\r\n        }\r\n    }</p><p>虽然这个效果groupbox也能实现，但是使用布局管理器的方式还是更加灵活和方便，这里有一个新的属性Layout.leftMargin，相信 不用解释大家也懂这个是设置什么的了。界面效果如下：</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86RxiAS1seAABcbVw1MkQ572.jpg"><p>3、GridLayout</p><p>GridLayout新增加的属性和之前讲过的Grid一样，就是行列以及sapcing的设置，其他的属性可以参考RowLayout。大家应该都遇到过登录界面，其实，登录界面的设计就是一个典型的GridLayout方法，我们写一个非常简单的登陆界面。</p><p> GridLayout{\r\n        anchors.centerIn: parent\r\n        columns: 2\r\n        rows: 3\r\n        columnSpacing: 20\r\n        rowSpacing: 20\r\n        Layout.rowSpan: 20\r\n    }</p><p>首先我们先定义网格布局管理器，设置其两列三行。第一行是用户名，第二行是密码，第三行就放置按钮类。接下里我们向网格中添加控件。</p><p>    //第一行\r\n    Label{\r\n        text:"用户名"\r\n        font.pixelSize: 20\r\n    }\r\n    Rectangle{\r\n         id:rect1\r\n         border.color: "black"\r\n         width: 200\r\n         height: 30\r\n         TextEdit{\r\n             anchors.fill: rect1\r\n             font.pixelSize: 20\r\n         }\r\n    }\r\n    //第二行\r\n    Label{\r\n        text:"密码"\r\n        font.pixelSize: 20\r\n    }\r\n    Rectangle{\r\n         id:rect2\r\n         border.color: "black"\r\n         width: 200\r\n         height: 30\r\n         TextEdit{\r\n             anchors.fill: rect2\r\n             font.pixelSize: 20\r\n         }\r\n    }\r\n    //第三行\r\n    Button{\r\n        text: "登录"\r\n        font.pixelSize: 20\r\n        Layout.leftMargin: -30\r\n        Layout.topMargin: 50\r\n    }\r\n    Button{\r\n        text: "退出"\r\n        font.pixelSize: 20\r\n        Layout.leftMargin: 140\r\n        Layout.topMargin: 50\r\n    }</p><p>这里用到了输入框TextEdit，先不给大家作讲解了，后续控件部分我们再了解它的用法。主要看代码对于最后一行的设置，这两个按钮，需要设置其相对位置距离上面的两行为50，登陆按钮的左边距为-30，退出按钮的左边距为140。具体的位置大家可以根据自己的窗口进行设置，这样，一个相对规整的登陆界面就完成了。</p><img src="http://file.elecfans.net/group1/M00/00/03/wKgNjl86RxiAIKDOAABNdtWY640929.jpg"><p>4、StackLayout</p><p>最后，我们要来说一下我们不太熟悉的StackLayout，它提供了一个一次只能显示一个项目堆栈。大家可以参考一下链接：它只包含</p><p>count</p><p></p><p>和</p><p></p><p>currentIndex</p><p></p><p>两个特有属性，前者用来获取子项目的数量，后者用来设置当前显示项目的索引。放在StackLayout中的子项目默认设置为了Layout.fillWidth和 Layout.fillHeight ，也就是说默认会填充整个布局区域。</p><p>StackLayout官方文档</p><p>大家可以down一下上面网站中的例程，通过修改currentIndex的值来显示当前的界面颜色，注意,currentIndex是从0开始的。大家也可以仿照例程自己添加几个矩形，来学习StackLayout的切换效果。然后我们在给出的例程中添加鼠标事件。</p><p>MouseArea {\r\n            anchors.fill: parent\r\n            onClicked: {\r\n                layout.currentIndex == 1 ?\r\n                            layout.currentIndex = 0\r\n                          : layout.currentIndex = 1\r\n            }\r\n        }</p><p>当点击鼠标时，通过对currentIndex的设置就可以实现界面的切换效果了！</p><p>总结</p><p>关于布局管理器的内容我们就讲解完毕了，每一种都有可能出现在你未来要开发的界面之中，有时间的小伙伴建议可以自己仿照一些简单的界面进行编写，来联系布局管理器的使用。在实际操作中学习，我们下节再会！</p><p>\r\n                    \t</p><p>Tab标签:</p><p>树莓派</p><p>  </p><p>Qt</p><p>                      </p>', 'video_id': '5fd4b97646af86f27adc629fcba870b2', 'tag': '', 'company': 454398, 'typeid': ''}
# dict_data = {'category': 'article', 'name': '树莓派Qt系列教程19：块编辑输入控件', 'url': 'https://www.waveshare.net/study/article-1022-1.html', 'abstract': '', 'content': '<p>前言</p><p>上一节中我们讲了单行编辑的控件，这一节我们再讲一下多行编辑，也就是块编辑控件，有两个，分别是块文本编辑器TextEdit和块文本编辑区域TextArea。</p><p>TextEdit</p><p>TextEdit显示一块可编辑的格式化文本，它可以显示纯文本和富文本。它的很多属性和方法和TextInput是一模一样的，最大的区别就是TextInput是显示一行数据，TextEdit是显示多行的数据。所以我们就不详细的讲解TextEdit了。</p><p>属性</p><p>主要是说一下和TextInput不同的属性和方法，相同的部分我们就不列举了，大家可以参考</p><p>链接</p><p>baseUrl</p><p></p><p>:&nbsp;url，指定用于解析文本中相对URL的基本URL</p><p>hoveredLink</p><p></p><p>:&nbsp;string，当用户悬停在文本中嵌入的链接时，此属性包含链接字符串。链接必须为富文本格式或HTML格式，并且链接字符串提供对特定链接的访问</p><p>lineCount</p><p></p><p>:&nbsp;int，返回TextEdit项中的总行数</p><p>selectByKeyboard</p><p></p><p>:&nbsp;bool，当编辑器可编辑时，默认为true；当只读时为false</p><p>textDocument</p><p></p><p>:&nbsp;TextDocument，</p><p>textFormat</p><p></p><p>:&nbsp;enumeration，文本显示的格式</p><p>textMargin</p><p></p><p>:&nbsp;real，TextEdit中文本周围的边距（以像素为单位）</p><p>信号</p><p>linkActivated(link)</p><p>：当用户单击文本中嵌入的链接时，将发出此信号</p><p>linkHovered(link)</p><p>：当用户悬停在文本中嵌入的链接时，将发出此信号</p><p>方法</p><p>void&nbsp;append(text)</p><p>：将带有文本的新段落追加到TextEdit的末尾</p><p>string&nbsp;getFormattedText(start,&nbsp;int&nbsp;end)</p><p>：返回介于开始位置和结束位置之间的文本部分</p><p>linkAt(x,&nbsp;real&nbsp;y)</p><p>：返回位于内容坐标中x，y处的链接字符串，如果该点不存在链接，则返回一个空字符串</p><p>例程</p><p>ColumnLayout{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;anchors.centerIn:&nbsp;parent<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spacing:&nbsp;50<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Rectangle{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width:&nbsp;300<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;height:&nbsp;300<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TextEdit&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id:&nbsp;textedit<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;height:&nbsp;parent.height<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;width&nbsp;:&nbsp;parent.width<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;padding:&nbsp;2<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textFormat:&nbsp;Text.RichText<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;font.family:&nbsp;"Helvetica"<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;font.pointSize:&nbsp;40<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;color:&nbsp;"blue"<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;focus:&nbsp;true<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wrapMode:TextEdit.WrapAnywhere<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RowLayout{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;spacing:&nbsp;95<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Button{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text:&nbsp;"clear"<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rightPadding:&nbsp;20<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;onClicked:&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textedit.clear()<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Button{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;text:&nbsp;"append"<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;onClicked:&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;textedit.append("Hello&nbsp;World!")<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}</p><p>用到了之前布局管理器的内容，我们先定义一个可视化的TextEdit，设置其属性textFormat文本显示的格式为富文本。</p><p>TextEdit.PlainText：（默认）所有样式标签均视为纯文本</p><p>TextEdit.AutoText：自动确定是否应将文本视为富文本格式</p><p>TextEdit.RichText：所有样式标签均视为富文本</p><p>TextEdit.MarkdownText：所有样式标签均视为MarkdownText</p><p>当一行文本要占用的宽度大于TextEdit的宽度时，wrapMode决定如何折行，它支持TextEdit.WordWrap(在单词边界处折行)、TextEdit.NoWrap(不折行，超出宽度的文本不显示)、TextEdit.WrapAnywhere(折行，不考虑单词边界)、TextEdit.&nbsp;Wrap(折行，尽量在单词边界处折行)4种换行策略。设其wrapMode为WrapAnywhere。然后在列布局管理器中定义两个按钮，点击时分别调用TextEdit的clear和append方法。</p><p>TextArea</p><p>TextArea是多行文本编辑区域，TextArea使用占位符文本功能拓展了TextEdit,并添加了修饰。TextArea的属性和TextField一模一样，只是方法上略有不同，详情可参考</p><p>链接</p><p>TextArea本身不能滚动，特别是在屏幕大小受限的平台上，通常最好使整个应用程序页面可滚动。但是，请注意，在这种情况下，TextArea的背景装饰会与其他可滚动内容一起滚动。如果要使TextArea可滚动，可以将其放在ScrollView中。</p><p>ScrollView&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;id:&nbsp;scView<br><br>&nbsp;&nbsp;&nbsp;&nbsp;anchors.centerIn:&nbsp;parent<br><br>&nbsp;&nbsp;&nbsp;&nbsp;width:&nbsp;200<br><br>&nbsp;&nbsp;&nbsp;&nbsp;height:&nbsp;100&nbsp;//&nbsp;初始高度就是一行<br><br>&nbsp;&nbsp;&nbsp;&nbsp;background:&nbsp;Rectangle&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;anchors.fill:&nbsp;parent<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;border.color:&nbsp;"gray"<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;radius:&nbsp;5<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;TextArea&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id:&nbsp;contentText<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wrapMode:&nbsp;TextArea.WrapAnywhere<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;font.pixelSize:&nbsp;25<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}</p><p>将TextArea放置在ScrollView中，设置ScrollView的大小，当我们输入文字超过ScrollView的宽度时，TextArea会自动换行。当超过其高度时，ScrollView会显示滚动栏。效果如下：</p><p>总结</p><p>对于块编辑输入控件来说，其实就是单行编辑输入控件的延伸，很多属性和方法都是一样的，大家可以和上一节对比着学习。关于编辑输入控件我们就学习到这儿了，下节再会！</p><p><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br></p><p>Tab标签:</p><p>&nbsp;</p><p>树莓派</p><p>&nbsp;&nbsp;</p><p>Qt</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>', 'video_id': '932e47b8fbdd09e4a92fff6a6f02b3ec', 'tag': '', 'company': 454398, 'typeid': ''}
dict_data = {'special': 7, 'author_id': 1, 'type': 'thread', 'title': '树莓派Qt系列教程19：块编辑输入控件',
             'url': 'https://www.waveshare.net/study/article-587-1.html', 'cur_time': 1597793913,
             'content': '<td id="article_content"><table border="0" cellpadding="0" cellspacing="0" style="color: rgb(72, 61, 139); font-family: Arial, 新宋体, 微软雅黑; font-size: 12px;" width="120"><tbody><tr><th align="center" bgcolor="#698192" class="white_12px_b" height="20" scope="col" style="color: rgb(255, 255, 255); line-height: 20px; font-family: Arial, Helvetica, sans-serif;">软件设计</th></tr></tbody></table><p class="type_666666_12px" style="font-size: 12px; line-height: 20px; color: rgb(102, 102, 102); border-top-style: none; font-family: Arial, Helvetica, sans-serif;">/*********************************************************************<br/>微 雪 电 子 WaveShare\xa0<a class="Blue_2_12px_" href="https://www.waveshare.net/" style="color: rgb(7, 66, 184);">http://www.waveShare.net</a>\xa0<br/>目 的: 建立PCF8563操作库<br/>目标系统: 基于STM8单片机\xa0<br/>应用软件: Cosmic CxSTM8\xa0<br/>说 明: 若用于商业用途，请保留此段文字或注明代码来源<br/>深 圳 市 微 雪 电 子 有 限 公 司 保 留 所 有 的 版 权\xa0<br/>*********************************************************************/</p><p class="type_666666_12px" style="font-size: 12px; line-height: 20px; color: rgb(102, 102, 102); border-top-style: none; font-family: Arial, Helvetica, sans-serif;">#include &lt;ws_i2c.h&gt;</p><p class="type_666666_12px" style="font-size: 12px; line-height: 20px; color: rgb(102, 102, 102); border-top-style: none; font-family: Arial, Helvetica, sans-serif;">void PS2_Init(void)<br/>{<br/>\xa0\xa0\xa0\xa0\xa0\xa0 PRESS_DATA_DDR;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 SHIFT_DATA_DDR;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 IN_SDA;<br/>}<br/><br/>void check(void)<br/>{<br/>\xa0\xa0\xa0\xa0\xa0\xa0 static u8 rcvBits = 0;\t//接收次数，中断次数<br/><br/>\xa0\xa0\xa0\xa0\xa0\xa0 OUT_SCK; //设置"SCK_DDR"为输出<br/>\xa0\xa0\xa0\xa0\xa0\xa0 DELAY();<br/>\xa0\xa0\xa0\xa0\xa0\xa0 SET_SCK;\t//"SCK_PORT"输出"1"<br/>\xa0\xa0\xa0\xa0\xa0\xa0 DELAY();<br/><br/>\xa0\xa0\xa0\xa0\xa0\xa0 IN_SCK;\t//设置"SCK_DDR"为输入<br/>\xa0\xa0\xa0\xa0\xa0\xa0 DELAY();<br/>\xa0\xa0\xa0\xa0\xa0\xa0 if(!GET_SCK)<br/>{<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 if((rcvBits&gt;0) &amp;&amp; (rcvBits&lt;9))<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 {\xa0<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 keyVal=keyVal&gt;&gt;1; //数据是LSB格式<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 //IN_SDA;\t//当有对键盘有属性设置时，注意输入输出切换<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 //DELAY();<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 if(GET_SDA)\xa0<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 keyVal=keyVal|0x80;\xa0<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 }<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 rcvBits++;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 while(!GET_SCK); //等待PS/2CLK拉高<br/><br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 if(rcvBits&gt;10)<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 {<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 rcvBits=0; //接收11次表示接收完一帧数据<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 rcvF=1; //标识有字符已经输入<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 }<br/>\xa0\xa0\xa0\xa0\xa0\xa0 }<br/>}<br/><br/>u8 keyHandle(u8 val)\xa0<br/>{<br/>\xa0\xa0\xa0\xa0\xa0\xa0 u8 i;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 static _Bool isUp=0;\t//按键释放动作标志<br/>\xa0\xa0\xa0\xa0\xa0\xa0 static _Bool shift=0;\t//shift键按下标志<br/>\xa0\xa0\xa0\xa0\xa0\xa0 rcvF = 0;\xa0<br/>\xa0\xa0\xa0\xa0\xa0\xa0 //PRESS_DATA_PORT = val;\t//状态指示<br/>\xa0\xa0\xa0\xa0\xa0\xa0 if(!isUp)<br/>\xa0\xa0\xa0\xa0\xa0\xa0 {<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 switch(val)<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 {<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 case 0xF0 :\t// a relase action<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 isUp = 1;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 break;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 case 0x12 :\t// Left shift<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 shift = 1;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 break;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 case 0x59 :\t// Right shift<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 shift = 1;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 break;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 default:<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 if(!shift)\t// If shift not pressed<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 {\xa0<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 //for(i=0; unshifted[i][0]!=val &amp;&amp; unshifted[i][0];i++);<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 //上述写法较为灵活，但KEIL非UNIXC标准，不能采用该写法<br/><br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 for(i=0;unshifted[i][0]!=val &amp;&amp; i&lt;59; i++);<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 if(unshifted[i][0] == val)\xa0<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 {<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 //SHIFT_DATA_PORT = val;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 val = unshifted[i][1];<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 return val;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 }<br/>//状态指示<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 }\xa0<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 else\t// If shift pressed<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 {\t<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 //for(i=0; unshifted[i][0]!=val &amp;&amp; unshifted[i][0]; i++);<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 //上述写法较为灵活，但KEIL非UNIXC标准，不能采用该写法<br/><br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 for(i=0;shifted[i][0]!=val &amp;&amp; i&lt;59; i++);<br/><br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 if(shifted[i][0] == val)\xa0<br/>{<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 //SHIFT_DATA_PORT = val;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 val = shifted[i][1];<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 return val;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 }//状态指示<br/><br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0 }<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 }<br/>\xa0\xa0\xa0\xa0\xa0\xa0 }<br/>\xa0\xa0\xa0\xa0\xa0\xa0 else\xa0<br/>\xa0\xa0\xa0\xa0\xa0\xa0 {<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 isUp=0;\t<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 switch(val)<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 {<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 case 0x12 :\t// Left SHIFT<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 shift = 0;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 break;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 case 0x59 :\t// Right SHIFT<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 shift = 0;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 break;<br/>\xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 \xa0\xa0\xa0\xa0\xa0\xa0 }<br/>\xa0\xa0\xa0\xa0\xa0\xa0 }<br/>\xa0\xa0\xa0\xa0\xa0\xa0 return 0xff;<br/>}</p></td>',
             'id': 'fc57b45793ef3881492a4136e83205db1', 'group_num': 1088}
# dict_data = {'category': 'article', 'name': '图SLAM：Noob的同时本地化和映射指南', 'url': 'https://www.cnblogs.com/panchuangai/p/13525937.html', 'abstract': '', 'content': '<div class="blogpost-body cnblogs-markdown" id="cnblogs_post_body">\n<p>作者|Krunal Kshirsagar<br/>\n编译|Flin<br/>\n来源|Medium</p>\n<p><img alt="" loading="lazy" /></p>\n<h3 id="什么是slam？">什么是SLAM？</h3>\n<p>即时定位与地图构建（simultaneous localization and mapping，简写成SLAM），用于环境模型（map）的并行构建，以及在其中移动的机器人的状态估算。换句话说，SLAM为你提供了一种实时跟踪机器人在世界上的位置、并识别地标（例如建筑物，树木，岩石和其他世界特征）的位置的方法。除了本地化之外，我们还希望建立机器人环境的模型，这样我们就有了一个物体的概念，以及围绕机器人的地标，以便我们可以使用此地图数据来确保机器人在世界各地移动时走在正确的道路上。因此，构建地图的关键是机器人本身可能会由于其运动不确定性而失去对其位置的跟踪，因为不存在现有的地图，并且我们正在用机器人并行构建地图。而这就是SLAM发挥作用的地方。</p>\n<h4 id="slam的工作：">SLAM的工作：</h4>\n<p>同时定位和地图绘制（SLAM）的基础是从机器人的传感器和随时间推移的运动中收集信息，然后使用有关测量和运动的信息来重建世界地图。在这种情况下，我们将机器人定位在2D网格世界中，因此，基于图的SLAM方法通过提取原始传感器测量值来构造简化的估计问题。这些原始测量值将替换为图中的边缘，然后可以将其视为虚拟测量值。<br/>\n假设我们有一个机器人和初始位置 <strong>x0 = 0</strong> 和 <strong>y0 = 0</strong> 。对于此示例，为了保持简单，我们并不关心方向。让我们假设机器人在X方向上向右移动了10。所以，在理想世界中，你会了解到 x1，运动后的位置与<strong>x0 + 10</strong>相同，即<strong>x1 = x0 + 10</strong>，同理，<strong>y1</strong>与<strong>y0</strong>相同。</p>\n<p>如图，机器人在x方向上的位移为10：</p>\n<p><img alt="" loading="lazy" src="http://file.elecfans.net/group1/M00/00/04/wKgNjl88z0iAYrCRAAE0fTtmb5s336.jpg"/></p>\n<p>但是根据<strong>卡尔曼滤波器</strong>（<a href="https://medium.com/@krunalkshirsagar/the-curious-case-of-kalman-filters-f29c3d17b121%EF%BC%89">https://medium.com/@krunalkshirsagar/the-curious-case-of-kalman-filters-f29c3d17b121）</a> 和其他各种机器人技术，我们已经知道<strong>位置实际上是不确定的</strong>。因此，与其假设我们的XY坐标系中的机器人精确地向右移动了10个位置，不如理解成它在x1 = x0 + 10运动更新后的实际位置是以（10,0）为中心的高斯分布，但是机器人也可能在其他地方。</p>\n<p>如图：运动更新后，高斯以机器人的位置为中心</p>\n<p><img alt="" loading="lazy" src="http://file.elecfans.net/group1/M00/00/04/wKgNjl88z0iAU8M2AAGWe0rLQjM431.jpg"/></p>\n<p>这是x变量的高斯的数学公式：<br/>\n当这两个条件相同时，与其将x1设置为x0+10，不如用高斯函数来表示，此时高斯函数达到峰值。。因此，如果你减去x1-x0-10，把它变成一个正方形，然后将其转换为高斯，我们将得到与x1和x0相关的概率分布。我们可以对y做同样的转换。根据我们的运动y不变，因此y1和y0尽可能靠近。</p>\n<p><img alt="" loading="lazy" src="http://file.elecfans.net/group1/M00/00/04/wKgNjl88z0iAEr4pAAI-wtyuDvs457.jpg"/></p>\n<p>这两个高斯的乘积现在是我们的约束条件。目标是在位置x0为（0,0）的情况下最大化位置x1的可能性。<strong>因此，Graph SLAM所做的是，它使用一系列此类约束条件来定义概率。</strong> 假设我们有一个在某个空间中移动的机器人，GRAPH SLAM会收集其初始位置（0,0），最初也称为“<strong>初始约束</strong>”，然后收集许多相对约束，这些相对约束会将每个机器人姿态与之前的机器人姿态相关联作为<strong>相对运动约束</strong>。例如，让我们使用机器人可以在各个位置看到的地标，这是每次机器人看到地标时的<strong>相对测量约束</strong>。<strong>因此，Graph SLAM收集这些约束，以便找到最可能的机器人路径配置以及地标位置，即映射过程。</strong></p>\n<p><img alt="" loading="lazy" src="http://file.elecfans.net/group1/M00/00/04/wKgNjl88z0iAPCMbAAKi78Ir9-I955.jpg"/></p>\n<h3 id="实作">实作</h3>\n<h4 id="生成环境：">生成环境：</h4>\n<p>我们将生成一个带有地标的2D世界网格，然后通过将机器人放置在该世界中，并在一定数量的时间步长上移动和感应来生成数据。实例化的机器人在世界中移动和感知时，将收集数据。我们的SLAM函数将把这些数据作为输入。因此，让我们首先创建此数据，并探索它如何代表我们的机器人进行运动和传感器测量。</p>\n<h4 id="slam输入：">SLAM输入：</h4>\n<p>除了数据之外，我们的slam函数还具有：</p>\n<ul>\n<li>N：机器人将要移动和感应的时间步数。</li>\n<li>num_landmarks：世界上的地标数量。</li>\n<li>world_size：你的世界的大小（w / h）。</li>\n<li>motion_noise：与运动相关的噪声；运动的更新置信度应为<code>1.0/motion_noise</code>.</li>\n<li>measurement_noise：与测量/传感相关的噪声；测量的更新权重应为<code>1.0/measurement_noise</code>.</li>\n</ul>\n<pre><code class="language-python">import numpy as np\nfrom helpers import make_data\n\n#slam的实现应该使用以下输入\n\n#请随意更改这些输入值并查看其响应方式！\n\n# 世界参数\nnum_landmarks      = 5        # number of landmarks\nN                  = 20       # time steps\nworld_size         = 100.0    # size of world (square)\n\n# 机器人参数\nmeasurement_range  = 50.0     # range at which we can sense landmarks\nmotion_noise       = 2.0      # noise in robot motion\nmeasurement_noise  = 2.0      # noise in the measurements\ndistance           = 20.0     # distance by which robot (intends to) move each iteratation \n\n\n# make_data实例化一个机器人，并为给定的世界大小和给定数量的地标生成随机地标\ndata = make_data(N, num_landmarks, world_size, measurement_range, motion_noise, measurement_noise, distance)\n\n</code></pre>\n<p>让我们编写两个主要功能，这些功能可以使机器人四处移动，帮助定位地标并在2D地图上测量它们之间的距离：</p>\n<ul>\n<li>Move：尝试按dx，dy移动机器人。</li>\n<li>Sense：返回可见范围内地标的x和y距离。</li>\n</ul>\n<pre><code class="language-python">class robot:\n    \n    #move function\n    def move(self, dx, dy):\n        \n        x = self.x + dx + self.rand() * self.motion_noise\n        y = self.y + dy + self.rand() * self.motion_noise\n        \n        if x &lt; 0.0 or x &gt; self.world_size or y &lt; 0.0 or y &gt; self.world_size:\n            return False\n        else:\n            self.x = x\n            self.y = y\n            return True\n    \n    \n    #sense function\n    def sense(self):\n        measurements = []\n\n        for landmark_index, landmark in enumerate(self.landmarks):\n            landmark_distance_x = landmark[0]\n            landmark_distance_y = landmark[1]\n            random_noise = self.rand()\n            cal_dx = self.x - landmark_distance_x + random_noise * self.measurement_noise\n            cal_dy = self.y - landmark_distance_y + random_noise * self.measurement_noise\n            is_not_in_measurement_range = self.measurement_range == -1\n            if(is_not_in_measurement_range) or ((abs(cal_dx) &lt;= self.measurement_range) and (abs(cal_dy) &lt;= self.measurement_range)):\n                measurements.append([landmark_index, cal_dx, cal_dy])\n        return measurements\n</code></pre>\n<h4 id="omega-和-xi：">Omega 和 Xi：</h4>\n<p>为了实现Graph SLAM，引入了一个矩阵和一个向量（分别为ω和xi）。矩阵是正方形的，标有所有机器人姿势（xi）和所有地标。例如，每次进行观察时，当你在两个姿势之间移动某个距离dx，并可以关联这两个位置时，可以将其表示为这些矩阵中的数值关系。<br/>\n让我们编写函数，以便它为机器人的起始位置返回omega和xi约束。我们尚不知道的所有值都应使用0进行初始化。我们可以假设我们的机器人以100％的置信度在世界的正中间开始。</p>\n<pre><code class="language-python">def initialize_constraints(N, num_landmarks, world_size):\n    \'\'\' This function takes in a number of time steps N, number of landmarks, and a world_size,\n        and returns initialized constraint matrices, omega and xi.\'\'\'\n    \n    middle_of_the_world = world_size / 2\n    \n    ## 建议：在变量中定义和存储约束矩阵的大小（行/列）\n    rows, cols = 2*(N + num_landmarks), 2*(N + num_landmarks)\n    ## TODO: 用两个初始“strength”值定义约束矩阵Omega\n    omega = np.zeros(shape = (rows, cols))\n    ## 我们机器人最初的x，y位置\n    #omega = [0]\n    \n    omega[0][0], omega[1][1] = 1,1\n    \n    ## TODO: Define the constraint *vector*, xi\n    ## 假设机器人以100％的置信度在世界的正中间开始。\n    #xi = [0]\n    xi = np.zeros(shape = (rows, 1))\n    xi[0][0] = middle_of_the_world\n    xi[1][0] = middle_of_the_world\n    \n    return omega, xi\n</code></pre>\n<h4 id="通过运动和测量值进行更新：">通过运动和测量值进行更新：</h4>\n<pre><code class="language-python">## slam接受6个参数并返回mu，\n## mu是机器人穿过的整个路径（所有x，y姿势）和所有地标位置\ndef slam(data, N, num_landmarks, world_size, motion_noise, measurement_noise):\n    \n    ## TODO: 使用你的初始化创建约束矩阵\n    omega, xi = initialize_constraints(N, num_landmarks, world_size)\n    ## TODO:遍历数据中的每个时间步骤\n    for time_step in range(len(data)):\n        \n        ## 每次迭代时获取所有的运动和测量数据\n        measurement = data[time_step][0]\n        motion = data[time_step][1]\n        x\n        dx = motion[0]         # 本次沿x移动的距离\n        dy = motion[1]         # 本次沿y移动的距离\n        \n        #假设机器人在这个时间从（x0，y0）移动到（x1，y1）\n        \n        #omega的偶数列对应于x值\n        x0 = (time_step * 2)   #x0 = 0,2,4,...\n        x1 = x0 + 2            #x1 = 2,4,6,...\n        \n        # omega 的奇数列对应于y值\n        y0 = x0 + 1            #y0 = 1,3,5,...\n        y1 = y0 + 2            #y1 = 3,5,7,...\n        \n        actual_m_noise = 1.0/measurement_noise\n        actual_n_noise = 1.0/motion_noise\n    ## TODO: 更新约束矩阵/向量（Omega/xi）以解释所有*measurements*\n    ## 这应该是一系列考虑测量噪声的附加值\n        for landmark in measurement:\n            lM = landmark[0]            # 地标 id\n            dx_lM = landmark[1]         # 沿x与当前位置分离\n            dy_lM = landmark[2]         # 沿y与当前位置分离\n            \n            L_x0 = (N*2) + (lM*2)       # 偶数列有x个地标值\n            L_y0 = L_x0 + 1             # 奇数列有y个地标值\n\n            # 更新对应于x0和Lx0之间测量值的omega值\n            omega[x0][x0] += actual_m_noise\n            omega[L_x0][L_x0] += actual_m_noise\n            omega[x0][L_x0] += -actual_m_noise\n            omega[L_x0][x0] += -actual_m_noise\n            \n            # 更新对应于y0和Ly0之间测量值的omega值\n            omega[y0][y0] += actual_m_noise\n            omega[L_y0][L_y0] += actual_m_noise\n            omega[y0][L_y0] += -actual_m_noise\n            omega[L_y0][y0] += -actual_m_noise\n            \n            # 更新X0和LX0之间的测量值对应的xi值\n            xi[x0]  -= dx_lM/measurement_noise\n            xi[L_x0]  += dx_lM/measurement_noise\n            \n            # 更新y0和Ly0之间的测量值对应的xi值\n            xi[y0]  -= dy_lM/measurement_noise\n            xi[L_y0] += dy_lM/measurement_noise\n            \n            \n        ## TODO: 更新约束矩阵/向量（omega/XI），以解释从（x0，y0）到（x1，y1）和运动噪声的所有*运动*。\n        omega[x0][x0] += actual_n_noise\n        omega[x1][x1] += actual_n_noise\n        omega[x0][x1] += -actual_n_noise\n        omega[x1][x0] += -actual_n_noise\n        \n        omega[y0][y0] += actual_n_noise\n        omega[y1][y1] += actual_n_noise\n        omega[y0][y1] += -actual_n_noise\n        omega[y1][y0] += -actual_n_noise\n        \n        xi[x0] -= dx/motion_noise\n        xi[y0] -= dy/motion_noise\n        \n        xi[x1] += dx/motion_noise\n        xi[y1] += dy/motion_noise\n    \n    ## TODO: 在遍历所有数据之后\n    ## 计算姿势和地标位置的最佳估计值\n    ##使用公式，omega_inverse * Xi\n    inverse_of_omega = np.linalg.inv(np.matrix(omega))\n    mu = inverse_of_omega * xi\n    \n    return mu\n</code></pre>\n<h4 id="机器人的姿势和地标：">机器人的姿势和地标：</h4>\n<p>让我们打印函数产生的估计姿势和界标位置。我们定义了一个提取姿势和地标位置，并将它们作为自己的单独列表返回。</p>\n<pre><code class="language-python">def get_poses_landmarks(mu, N):\n    # 创建一个姿势列表\n    poses = []\n    for i in range(N):\n        poses.append((mu[2*i].item(), mu[2*i+1].item()))\n\n    # 创建一个地标列表\n    landmarks = []\n    for i in range(num_landmarks):\n        landmarks.append((mu[2*(N+i)].item(), mu[2*(N+i)+1].item()))\n\n    # 返回完成的列表\n    return poses, landmarks\n  \ndef print_all(poses, landmarks):\n    print(\'/n\')\n    print(\'Estimated Poses:\')\n    for i in range(len(poses)):\n        print(\'[\'+\', \'.join(\'%.3f\'%p for p in poses[i])+\']\')\n    print(\'/n\')\n    print(\'Estimated Landmarks:\')\n    for i in range(len(landmarks)):\n        print(\'[\'+\', \'.join(\'%.3f\'%l for l in landmarks[i])+\']\')\n\n# 调用你的slam实现，并传入必要的参数\nmu = slam(data, N, num_landmarks, world_size, motion_noise, measurement_noise)\n\n# 打印出地标和姿势结果\nif(mu is not None):\n    # 获取姿势和地标列表\n    # 并打印出来\n    poses, landmarks = get_poses_landmarks(mu, N)\n    print_all(poses, landmarks)\n</code></pre>\n<p>如图：估计的机器人姿势和地标<br/>\n<img alt="" loading="lazy" src="http://file.elecfans.net/group1/M00/00/04/wKgNjl88z0iATgOGAABJQiSn5ZE767.jpg"/></p>\n<h4 id="可视化构建的世界：">可视化构建的世界：</h4>\n<pre><code class="language-python"># 导入函数\nfrom helpers import display_world\n\n# 显示最终世界！\n\n# 定义图形大小\nplt.rcParams["figure.figsize"] = (20,20)\n\n# 检查姿势是否已创建\nif \'poses\' in locals():\n    # 打印出最后一个姿势\n    print(\'Last pose: \', poses[-1])\n    # 显示机器人的最后位置和地标位置\n    display_world(int(world_size), poses[-1], landmarks)\n</code></pre>\n<p>如图：输出量<br/>\n<img alt="" loading="lazy" src="http://file.elecfans.net/group1/M00/00/04/wKgNjl88z0iAEhQxAAEPBVzJhI8496.jpg"/></p>\n<p>在Github（<a href="https://github.com/Noob-can-Compile/Landmark_Detection_Robot_Tracking_SLAM-%EF%BC%89">https://github.com/Noob-can-Compile/Landmark_Detection_Robot_Tracking_SLAM-）</a> 上检查代码。</p>\n<p>原文链接：<a href="https://medium.com/@krunalkshirsagar/graph-slam-a-noobs-guide-to-simultaneous-localization-and-mapping-aaff4ee91dee">https://medium.com/@krunalkshirsagar/graph-slam-a-noobs-guide-to-simultaneous-localization-and-mapping-aaff4ee91dee</a></p>\n<p>欢迎关注磐创AI博客站：<br/>\n<a href="http://panchuang.net/">http://panchuang.net/</a></p>\n<p>sklearn机器学习中文官方文档：<br/>\n<a href="http://sklearn123.com">http://sklearn123.com/</a></p>\n<p>欢迎关注磐创博客资源汇总站：<br/>\n<a href="http://docs.panchuang.net/">http://docs.panchuang.net/</a></p>\n</div>', 'video_id': '20bee57d728ead49e41654a5b9e1efff1', 'tag': '', 'company': 454398, 'typeid': ''}
# dict_data = {'category': 'article', 'name': '图片一缩放，AI就把熊猫“认”成长臂猿了，这么傻的AI该如何保护？', 'url': 'https://www.ofweek.com/ai/2020-08/ART-201717-11001-30453468_3.html', 'abstract': '', 'content': '<div class="artical-content" id="articleC">\n<p style="text-indent: 2em; text-align: left;">在实际生活中，我们通常不会期望一个图像在经过缩小后变成另外一个模样完全不同的图像，但这样奇怪的事情可能会在<span class="hrefStyle"><a href="https://www.ofweek.com/ai/" target="_blank" title="人工智能">人工智能</a></span>领域发生。</p><p style="text-indent: 2em; text-align: left;">来自德国 Braunschweig 技术大学的研究人员通过大量实验已经证明，仔细修改数码照片的像素值可以使照片在缩小尺寸后变成与之前完全不同的图像，而这些对图像的修改操作在人工智能算法领域的影响值得被广泛关注。</p><p style="text-indent: 2em; text-align: left;">图像缩放技术在人工智能研究领域有着十分重要的地位，但是也存在一些挑战。其主要的问题就是，恶意攻击者可以利用这种图像缩放技术，对用于人脸识别、目标检测等<span class="hrefStyle"><a href="https://www.ofweek.com/ai/CAT-201716-cpv.html" target="_blank" title="计算机视觉">计算机视觉</a></span>方向的机器学习模型发起对抗性攻击。</p><p style="text-align:center"><img src="http://file.elecfans.net/group1/M00/00/06/wKgNjl88_QyASYjxAASDe9KrEZM763.jpg"/></p><p style="text-indent: 2em; text-align: left;">其中，对抗性机器学习是一种对数据进行操作的技术，它能在不被人类察觉的情况下改变人工智能算法的行为，而创建对抗性的机器学习示例是一个反复试验的过程。创建对抗性示例包括对图像像素进行细微的调整，再通过 AI 算法重新运行该图像，以查看图像置信度的变化。通过适当调整后，可以自动化创建一个噪音映射（noise map）来降低一个类的置信度，而提高另一个类的置信度。</p><p style="text-indent: 2em; text-align: left;">在今年 Usenix 安全研讨会上发表的一篇论文中，TU Braunschweig 的研究人员就针对机器学习系统的分级和防止对抗性图像缩放攻击进行了深入的回顾。他们的发现不断提醒我们，AI 算法许多隐藏的方面和威胁还未被发现，导致这些影响在我们的日常生活中正变得越来越突出。</p><p style="text-indent: 2em; text-align: left;"><strong>对抗性图像缩放</strong></p><p style="text-indent: 2em; text-align: left;">当在许多实例上训练时，机器学习模型创建不同类之间相似性程度的数学表达。例如，如果你训练一个机器学习算法来区分熊猫和长臂猿，它就会尝试创建一个统计模型来区分新图像中的像素是更像熊猫还是长臂猿。</p><p style="text-indent: 2em; text-align: left;">实际上，这些人工智能算法学习区分不同物体的方式与人类视觉的工作方式不同。大多数对抗性攻击利用这种差异，在改变机器学习系统输出的同时，进行人类肉眼无法察觉的细微调整。</p><p style="text-indent: 2em; text-align: left;">例如，当你让一个人描述他是如何从图片中发现熊猫的，他可能会寻找一些目标的身体特征，比如眼睛周围的黑色毛发，黑白相间的皮毛以及体型大小。他可能还会给出其他的背景，比如他希望看到熊猫在什么样的栖息地，会摆出什么样的动作姿势等等。</p><p style="text-indent: 2em; text-align: left;">而对于人工神经网络来说，只要根据公式，通过计算机程序运行图像的像素值提供正确的答案，就确信所看到的图像确实是一只熊猫。换句话说，通过正确地调整图像中的像素值，你也可以让 AI 误以为它看到的不是熊猫。</p><p style="text-indent: 2em; text-align: left;">其中的细节在于，研究人员在图像上添加了一层人眼几乎觉察不到的噪声。</p><p style="text-align:center"><img src="http://file.elecfans.net/group1/M00/00/06/wKgNjl88_QyAXstfAAGV85Yrer8753.jpg"/></p><p style="text-indent: 2em; text-align: left;">当新的像素值通过人工神经网络时，会产生从长臂猿的图像中所期望的结果。而在人眼看来，左右两幅图像似乎是同一只熊猫。</p><p style="text-align:center"><img src="http://file.elecfans.net/group1/M00/00/06/wKgNjl88_QyAGlQ8AAAxD_Cnpxw985.jpg"/></p><p style="text-indent: 2em; text-align: left;">研究人员在他们的论文中写道：“尽管大量研究都在研究针对学习算法的攻击，但目前为止，几乎没有人关注机器学习预处理中的漏洞。”但是，当经典的对抗性攻击利用人工智能算法内部工作的特性时，图像缩放攻击就集中在机器学习流水线的预处理阶段。</p>\n</div><div class="artical-content" id="articleC">\n<p style="text-indent: 2em; text-align: left;"><strong>图像缩放攻击</strong><br/></p><p style="text-indent: 2em; text-align: left;">对于输入数据来说，每一个应用于图像处理方向的机器学习算法都有一系列的要求。这些要求主要包括图像的特定大小，但其他因素（如颜色通道的数量和颜色深度）也可能会被涉及到。</p><p style="text-indent: 2em; text-align: left;">无论你是在训练一个机器学习模型，还是用该模型进行推理（分类、目标检测等），都需要对输入图像进行预处理以满足 AI 的输入要求。根据以上提及的所有需求，我们可以假定预处理过程通常需要将图像缩放到合适的大小。并且，就像我们通常在软件中遇到的情况一样，当黑客知道一个程序（或者至少是程序的一部分）工作过程的时候，他们会试图找到方法修改程序来谋取私利。而这就是图像缩放攻击发挥作用的地方。</p><p style="text-indent: 2em; text-align: left;">图像缩放攻击的关键思想是在预处理阶段通过调整图像像素大小来改变输入图像的外观。事实上，机器学习和深度学习大多都使用一些众所周知且有文档记载的缩放算法。这些算法中的大多数（比如最近邻插值和双线性插值）都和 Photoshop 等用于图像编辑的应用程序使用的算法一样，这使得攻击者更容易设计出同时适用于多种机器学习算法的漏洞。</p><p style="text-indent: 2em; text-align: left;">当图像被缩小时，进行缩放后的图像的每个像素都是源图像中像素块值的组合，其中执行这种转换的数学函数称为“核函数”。然而，并不是源像素块中的所有像素在核函数中的贡献是相等的（如果相等会使得调整后的图像会变得太模糊）。因此，在大多数算法中，核函数将更大的权重赋给更接近源像素块中间的像素。</p><p style="text-indent: 2em; text-align: left;">在对抗性预处理中，攻击者获取一幅图像，并在正确的位置对像素值进行适当调整。当图像经过缩放算法后，它会变成目标图像。最后，再用机器学习算法处理修改后的图像。基本上，人眼看到的是源图像，而机器学习模型看到的是目标图像。</p><p style="text-align:center"><img src="http://file.elecfans.net/group1/M00/00/06/wKgNjl88_QyALoxEAAPHpyOFoIU256.jpg"/></p><p style="text-indent: 2em; text-align: left;">当攻击一个机器学习模型时，攻击者必须知道所使用的大小调整算法的类型和核函数的核大小。由于大多数机器学习库中只有很少的缩放选项，因此研究人员通过实验发现攻击者只需尝试几次就能得到正确的参数设置。</p><p style="text-indent: 2em; text-align: left;">在 TechTalks 的评论中，IBM Research 的首席科学家 Chen Pin－Yu 将图像缩放与隐写术（steganography）进行了比较，后者将消息（这里是缩小后的图像）嵌入源图像中，只能采用缩小后的算法进行解码。</p><p style="text-indent: 2em; text-align: left;">写过几篇关于对抗性机器学习论文的 Chen 说：“我很好奇这种攻击是否也与图像缩放算法无关。但基于通用摄动的成功，我认为通用图像缩放攻击也是可行的。”</p><p style="text-indent: 2em; text-align: left;"><strong>图像缩放攻击示例</strong></p><p style="text-indent: 2em; text-align: left;">对于机器学习算法方向的图像缩放攻击主要有两种情况。其中一种攻击类型是创建在经过训练后的机器学习算法中产生错误预测的对抗性实例。但研究人员在他们的论文中指出，或许“数据中毒”攻击才是图像缩放的更大威胁。</p><p style="text-indent: 2em; text-align: left;">而“数据中毒”是一种对抗性攻击，在当机器学习模型调整其参数到图像的成千上万像素这一训练阶段时发生。如果攻击者能够访问并篡改训练中使用的数据集，就能够让机器学习模型在对抗性示例上进行训练。</p><p style="text-indent: 2em; text-align: left;">示例一：</p><p style="text-indent: 2em; text-align: left;">假设有一家公司正在开发一种面部识别系统，以控制在处理敏感材料区域的访问权限。为了做到这一点，该公司的工程师们正在训练一个卷积神经网络来检测授权员工的脸部。</p><p style="text-indent: 2em; text-align: left;">当团队正在收集训练数据集时，一个有意破坏的员工偷偷地将一些篡改过的图像隐藏在未经授权的员工的面部照片中。在训练了神经网络之后，工程师们为了确保系统能正确地检测到被授权的员工，对其进行测试。他们还会检查一些随机图像，以确保 AI 算法不会将访问权限错误地授予给非授权人员。</p><p style="text-indent: 2em; text-align: left;">但这就存在一个问题，只有他们明确地检查对抗性攻击中被攻击者脸部的机器学习模型，他们才会发现被恶意篡改的数据。</p><p style="text-align:center"><img src="http://file.elecfans.net/group1/M00/00/06/wKgNjl88_Q2AQK12AAImT-dPW5E819.jpg"/></p>\n</div><div class="artical-content" id="articleC">\n<p style="text-indent: 2em; text-align: left;">示例二：<br/></p><p style="text-indent: 2em; text-align: left;">假设你正在训练一个神经网络来识别停车标志的图像，以便以后在自动驾驶汽车时使用。</p><p style="text-indent: 2em; text-align: left;">恶意攻击者可以破坏训练数据，使其包含经过修补的停车标志图像，这些图像被称为“对抗性补丁”。经过训练后，神经网络把该补丁上的所有标志与目标联系起来。</p><p style="text-indent: 2em; text-align: left;">这样会导致自动驾驶汽车把一些随机的标志当作停车标志，或者更糟的是，进行错误分类并绕过真正的停车标志。</p><p style="text-align:center"><img src="http://file.elecfans.net/group1/M00/00/06/wKgNjl88_Q2AI8x7AALYLHusgdk075.jpg"/></p><p style="text-indent: 2em; text-align: left;"><strong>保护机器学习模型</strong></p><p style="text-indent: 2em; text-align: left;">TU Braunschweig 的研究人员在其论文中强调，因为大多数机器学习模型使用的是少数流行的图像缩放算法之一，所以图像缩放攻击对 AI 来说是一个特别严重的威胁。</p><p style="text-indent: 2em; text-align: left;">这使得图像缩放攻击“与模型无关”，意思就是它们对目标<span class="hrefStyle"><a href="https://www.ofweek.com/ai/" target="_blank" title="人工智能">人工智能</a></span>算法类型不敏感，而单一的攻击方案可以应用于整个范围的机器学习算法。相比之下，经典的对抗性例子是为每种机器学习模型设计的，如果目标模型发生轻微变化，攻击极有可能不再有效。</p><p style="text-indent: 2em; text-align: left;">Chen 在论文中说道：“与白盒对抗性攻击相比，图像缩放攻击需要更少的信息（只需要知道目标系统使用了何种缩放算法），所以从攻击者的角度来看，它是一种更实用的攻击。”“然而，它仍然没有不需要目标机器学习模型信息的黑盒对抗性攻击实用。”其中，黑盒对抗性攻击是一种通过观察机器学习模型的输出值来产生对抗性扰动的高级技术。</p><p style="text-indent: 2em; text-align: left;">Chen 接着在文章中承认，图像缩放攻击确实是一种生成对抗性实例的有效方法。但他补充道，缩放操作并不是都出现在每个机器学习系统中。他说：“图像缩放攻击仅限于基于图像且具有缩放操作的模型，但是在没有缩放操作和其他数据模式的图像模型中也可能存在对抗性实例。”对抗性机器学习也适用于音频和文本数据。</p><p style="text-align:center"><img src="http://file.elecfans.net/group1/M00/00/06/wKgNjl88_Q2AKTJ4ABey3-DaiGg791.jpg"/></p><p style="text-indent: 2em; text-align: left;">从积极的角度来看，对抗性图像缩放的单一性使得更好地检查攻击和开发保护机器学习系统的新技术成为可能。</p><p style="text-indent: 2em; text-align: left;">TU Braunschweig 的研究人员在文中写道：“由于机器学习模型的复杂性，针对学习算法的攻击仍然难以分析，但定义坚挺的缩放算法结构使得我们能更全面地分析缩放攻击并开发有效的防御技术。”在他们的论文中，研究人员提供了几种阻挠对抗性图像缩放攻击的方法，包括平滑核函数的权重缩放算法以及可以消除篡改像素值影响的图像重建过滤器。</p><p style="text-indent: 2em; text-align: left;">“我们的工作为机器学习中预处理的安全性提供了新的见解，”研究人员写道。“我们相信，有必要进行深入的研究工作，从而确定和排除数据处理不同阶段的漏洞，同时加强以学习为基础的系统的安全性能。”让机器学习算法对对抗性攻击具有鲁棒性已成为近年来一个较为活跃的研究领域。对抗性实例除了用于攻击之外也被用于模型训练，以增强模型的健壮性。因此，为了进行对抗性模型训练，不同类型的对抗性攻击实施是有益的。</p>\n</div>', 'video_id': 'bb332686a2afa798c2490270ed6503074', 'tag': '', 'company': 454398, 'typeid': ''}

# url = config.main_interface_domain + '/d/article/apipicker'
# url = config.bbs_interface_domain + '/home.php?mod=misc&ac=ajax&op=publish'
# print(url)
# headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
#                          'Chrome/44.0.2403.157 Safari/537.36'}
# resp = s.post(url, data=dict_data)
# print(resp.status_code)
# print("---------", resp.text)
# print("111111111")
# # tracker_path = get_tracker_conf('E:\PycharmProjects\Job_code\spider\database\client.conf')  # 绝对路径
# client = Fdfs_client(client_config_path)
# img_path = os.path.join(config.APP_ROOT, settings['IMAGES_STORE'] + str(uuid.uuid1()))
# ret_upload = client.upload_by_filename('E:\PycharmProjects\Job_code\spider\imgs/' + str(1) + '.jpg')
#
# img = "<img " + "src="'"' + config.LOCAL_HOST_PREFIXES + str(ret_upload['Remote file_id'],
#                                                               encoding="utf8") + '"'">"
# print(img)
proxy_url = "http://proxy.elecfans.net/proxys.php?key=nTAZhs5QxjCNwiZ6&num=90"
# proxy_url = "https://api.bug.im/udnQz0606wFogr/10-china-0-http-high_anonymous-json"
ips = requests.get(proxy_url).text
ips = json.loads(ips)['data']
print(ips)
# proxy = random.choice(ips['data'])['ip']
proxy = {'http': random.choice(ips)['ip']}
print(proxy)
# ip = {'http': proxy}
# print(ip)
response = requests.get(proxy_url, proxies=proxy)


# # dict_data = {'category': 'article', 'name': 'LIDAR有哪些主要设计考量？', 'url': 'https://www.ednchina.com/news/2020072416623.html', 'abstract': '', 'content': '<p>成功的自动驾驶汽车必定将使用紧密集成的传感器系统来达到甚至超越人类的驾驶能力。人类驾驶员一般利用双眼、双耳，以及车辆运动给人的反馈来驾驶汽车。我们的大脑会实时处理所有这些信息，并从人脑的驾驶经验数据库中直觉反应。复现人类驾驶能力所需的传感器包括雷达、激光雷达(LIDAR)、摄像头、惯性测量单元(IMU)，以及超声波传感器。每种系统都有其优势，也有其缺点。单一传感器的精度和性性能不足以取代所有其他传感器相辅相成的多传感器融合是大趋势。本文将介绍与LIDAR有关的主要设计考量，LIDAR是一种传感器，为各种自动驾驶解决方案提供大量数据。</p><img src="http://file.elecfans.net/group1/M00/00/01/wKgNjl8tNtKAAuNzAADXfr_502I688.jpg"><p>图1.蛛网图，比较视觉、雷达和LIDAR。</p><p>在自动驾驶汽车中，LIDAR与雷达密切合作。这两种技术在工作中不会产生可见光，这对于夜间驾驶或弱光条件至关重要。雷达适合远距离检测和跟踪，LIDAR则提供更高的角分辨率，可以识别对象并对其进行分类。换句话说，雷达适合检测对象是否存在，LIDAR则能够在雷达检测到对象的基础上提供关于该对象的具体信息。</p><img src="http://file.elecfans.net/group1/M00/00/01/wKgNjl8tNtKAUvk1AACpEXYjnzc690.jpg"><p>图2.自动驾驶汽车的LIDAR感知。</p><p>设计LIDAR系统时会面临一些技术挑战，主要挑战之一就是近红外波长要保持在人眼安全限值之下。关于这些安全指南，请参考IEC60825-1。这并不是要降低人眼安全的重要性，本文探讨的所有方面最终都会影响人眼安全。目前存在多种不同的LIDAR系统技术，设计复杂程度各不相同，各有其优点和缺点。</p><p>重要的是，所有设计需要关注的基础方面都相同。再此我们着重讨论人眼安全以外影响系统设计的其他方面，包括：SNR最大化、最小可检测要求、视场、散热、功耗，以及航位推测。</p><p>查看接收链路，会发现系统的信噪比(SNR)会影响在远距离（100米至300米）检测小型目标的能力。ADC噪底不能超过接收路径中的其他噪声源。如果背景光或散粒噪声贡献因素低于ADC的噪底或印刷电路板(PCB)噪声，系统精度就会受限。采用直接飞行时间(ToF)法要求系统可以输出短脉冲（~1ns至5ns），且使用高采样速率ADC检测这些脉冲。采样速率达到1GSPS，即可满足接收信号链路需求。此外，请注意，ADC的有效位数(ENOB)必须支持跨阻放大器(TIA)的整个输出范围，不能对信号实施削波。</p><p>系统需要检测100米开外的篮球吗？确定相关对象的反射率、尺寸和距离，会决定TIA的SNR需求。与ADC相同，TIA也需要检测同样的窄脉冲。因为系统需要处理的对象的距离、反射率和尺寸范围甚广，所以TIA必须能够在饱和后中快速恢复。高反射（比如交通标志怕爱）或近距离目标能反射强光而造成TIA饱和，。这些都是常见事件，而系统恢复的速度（以尽量减少致盲时间）对于安全性至关重要。</p><img src="http://file.elecfans.net/group1/M00/00/01/wKgNjl8tNtKAUkqfAAE5BIz9I-o503.jpg"><p>图3.LIDAR电气架构。</p><p>系统的视场和角分辨率也会影响到检测一个篮球的能力。发射和接收光学是决定视场的主要因素。角分辨率决定您能远距离检测篮球大小的目标并进行分类，还是只能检测目标的存在。</p><p>对于LIDAR系统设计人员来说，处理这些系统的功耗和散热是不小的挑战。当然，降低信号链的功耗会相应降低产生的热量。组件在的性能随温度会出现大幅变化，其中更敏感的一些组件可能需要温度补偿。使用热电控制器是一种不错的方法，可以高度准确地对IC进行冷却或加热。如果追求精确的话，发光和感光二极管都需要温度补偿，以在LIDAR系统的工作温度范围内保持稳定的工作波长和效率。</p><p>在某些情况下，对雪崩光电二极管和激光器施加的偏置电压要到几百伏（正压或负压）。高效生成这些电压，且使用尽可能少的组件，是最佳设计实践所追求的。要提供准确的基准电压源，需要使用精密数模转换器(DAC)生成偏置点、电流和电压。沿着传统的1.8V至12V电压域，LIDAR系统对电压的需求是有所增加的。小心选择电源解决方案可以处理这一问题，尤其是解决方案中额外添加一种电压时。选择具备关断或低功率模式的IC和电源也非常重要，这样的系统可以灵活高能效地轮询多个通道。</p><p>集成LIDAR传感器的IMU具备多种优势。IMU传感器智能地融合多轴陀螺仪和加速度计，为除震和导航应用提供可靠的位置和运动识别。即使在复杂的操作环境下，面临极限运动动力学问题时，精密微机电系统(MEMS)IMU也能提供所需的精度。</p><p>IMU为自动驾驶系统提供航位推测、定位和稳定功能。反过来，在ADAS或GPS性能下降，或者不可用时，上述功能又能为系统提供可靠的数据。IMU能够有效利用高更新速率（每秒数千个样本），且可以不受外部环境变化干扰。IMU越稳定，越能长时间为系统提供关键且可靠的航迹信息。</p><p>IMU可以直接集成到LIDAR模块中，用于检测、分析和纠正车辆运行环境中常见的振动。例如，IMU输出可辅助拼接LIDAR点云，否则，这些点云会因为车辆越过路面坑洼而出现偏离。此外，还可以使用IMU检测旋转式LIDAR系统的轴承磨损，以在实际出现故障之前维修LIDAR，提高安全性。</p><p>结论</p><p>在最初产品定义期间，需要考虑LIDAR系统的复杂性，以确定可接受的SNR、检测要求、视野、散热限制以及功耗。了解哪些组件是各个问题的主要贡献因素，同时谨慎选择IC，可以大大提高设计的成功几率。</p><p>作者简介</p><p>SarvenIpek于2006年加入ADI公司。在ADI公司任职期间，Sarven在故障分析、设计、特性表征、产品工程、项目管理和程序管理方面积累了丰富的经验。Sarven目前是ADI公司麻萨诸塞州威明顿市自动驾驶和安全产品部门LIDAR部的营销经理。</p><p>他拥有东北大学电气和计算机工程学士学位以及电气工程硕士学位，主修通信系统和信号处理。</p>', 'video_id': 'a2bf8d77bd7c2d125f3d3447cd3ea781', 'tag': '', 'company': 454398, 'typeid': ''}
# dict_data = {'category': 'article', 'name': '光线追踪：一种颠覆性技术', 'url': 'https://www.ednchina.com/news/200202201022.html', 'abstract': '', 'content': '<p>对于任何名副其实地从事AR/VR/XR、产品设计或仿真工作的工程师而言，光线追踪是他们应该熟悉的一种技术。因为它是自三维（3D）图形诞生以来图形技术领域最重要的进步之一，而且它即将从高深的电影和广告领域转向移动、可穿戴和汽车等嵌入式领域，作为全新的、更有效的处理光线追踪的方法进入市场。</p><p>如果你去看任何的三维场景，会发现其逼真度很大程度上取决于光照。在传统的图形渲染（光栅化处理）中，光照贴图和阴影贴图是预先计算好的，然后应用到场景中以模拟场景外观。然而，虽然可以实现很美观的效果，但其始终受限于一个事实，即这些技术仅仅是在模拟光照。光线追踪技术则是模拟光照在真实世界中如何表现，以创建更精确、更程序化的反射、透明、发光和材质图像。</p><p>在现实生活中，光源发出的虚拟光束会照射到物体上。然后光线会与该物体相互作用，并根据物体的表面性质再反射到另一个表面上。之后，光线会不停地进行反射，从而产生光和影。</p><img src="http://file.elecfans.net/group1/M00/00/02/wKgNn18zr_mAIfuMAACp3Doj-hg540.jpg"><p>计算机中的光线追踪，或者更准确地说是“路径追踪”，其过程与真实世界中的光线照射路径是相反的。光线实际上是从摄像机的视角发射出来，照射到场景中的物体上，然后算法会根据光线所照射到的表面的性质来计算光线将如何与该表面相互作用。之后，会继续追踪每条光线照射到每个物体上的路径，直至返回光源。结果就是一个场景被照亮，就像其被真实世界中的太阳照亮一样：具有逼真的反射和阴影效果。</p><img src="http://file.elecfans.net/group1/M00/00/02/wKgNn18zr_mAL1HVAAC43GcGUyU198.jpg"><p>传统上，由于计算负载太高，无法在嵌入式设备（甚至高端工作站）上实时地执行此操作。</p><p>虽然光线追踪在游戏中还是非常新鲜的事物，但许多人已经通过三维动画电影熟悉了光线追踪。在《玩具总动员4》（ToyStory4）的开场镜头中，雨坑里反射的光线就是最近的一个例子。然而，这些场景需要在专用服务器集群上用数月时间去渲染，这对于游戏来说并不适用，在游戏中场景必须以至少每秒30帧的速度实时生成，最好是两倍速度甚至更高。</p><p>在游戏中进行实时光线追踪以前是无法实现的，因为涉及巨大的计算量，但是这种情况已经有所改变，这要归功于将光栅化的速度和光线追踪的视觉精确度结合在一起的混合式方法。在你的手机上运行具有光线追踪功能的游戏将会有多棒？这将在不久的将来成为现实。</p><p>有这样一个问题，尽管用于移动设备的实时光线追踪解决方案已经存在了一段时间，但是仍没有一个生态系统来支持它——不过这种情况正在改变。2018年，英伟达（NVIDIA）面向台式电脑市场，尤其是游戏玩家发布了具有混合实时光线追踪功能的硬件。但是就连英伟达在发布该硬件时也没有任何游戏，这突显出为一种新技术打造生态有多么困难。然而，现在诸如Bethesda和Unity等众多游戏开发商已经有所行动，同时2020年的新一代游戏机也将包括一些光线追踪功能。不久之后，光线追踪亦会开始出现在其他市场中——例如AR/VR市场。</p><p>结果是，随着光线追踪重新被提上日程以及人们开始在自己的电脑上体验它，大家会逐渐想要去拥有它，并且确实期望它出现在自己的移动设备、VR耳机和游戏机上。因此，为了跟上时代的步伐，工程师们需要开始熟悉这一改变行业的技术的最新进展。</p><p>（责编：DemiXia）</p>', 'video_id': '73479e1a4e6e31ad1fa93f3713a3bdd7', 'tag': '', 'company': 454398, 'typeid': ''}
#
# url = config.main_interface_domain + '/d/article/apipicker'
# print(url)
# headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
#                                          'Chrome/44.0.2403.157 Safari/537.36'}
# resp = s.post(url, data=dict_data, headers=headers)
# print(resp.status_code)
# print("---------",resp.text)
# print("111111111")
# tracker_path = get_tracker_conf('E:\PycharmProjects\Job_code\spider\database\client.conf')  # 绝对路径
# client = Fdfs_client(client_config_path)
# img_path = os.path.join(config.APP_ROOT, settings['IMAGES_STORE'] + str(uuid.uuid1()))
# ret_upload = client.upload_by_filename('E:\PycharmProjects\Job_code\spider\imgs/' + str(1) + '.jpg')
#
# img = "<img " + "src="'"' + config.LOCAL_HOST_PREFIXES + str(ret_upload['Remote file_id'],
#                                                               encoding="utf8") + '"'">"
# print(img)
